include "_aiHeader.xs";
include "_aiQueries.xs";
include "_aiPlans.xs";
include "_aiForecasts.xs";


void main(void)
{
    if (aiGetGameType() != cGameTypeRandom)
    {
        if (aiGetGameType() == cGameTypeSaved)
        {
            for(player = 1 ; < cNumberPlayers)
            {
                if (kbIsPlayerHuman(player) == false)
                    continue;
                aiChat(player, "Hey "+kbGetPlayerName(player)+", due to some technical limitations, we've decided not to support saved games.");
                aiChat(player, "So I just wanted you to know that this AI is probably going to have weird behavior. Sorry ^_^ -- AlistairJah");
            }
        }
        else
        {
            for(player = 1 ; < cNumberPlayers)
            {
                if (kbIsPlayerHuman(player) == false)
                    continue;
                aiChat(player, "Hey "+kbGetPlayerName(player)+", this AI wasn't written to support custom scenarios and camptaigns. Chances are it will not work as you expect it to work.");
                aiChat(player, "If you need help with custom AIs, hit me up in the official Discord server of Wars of Liberty ^_^ -- AlistairJah");
            }
        }
    }
    if (aiTreatyActive())
    {
        for(player = 1 ; < cNumberPlayers)
        {
            if (kbIsPlayerHuman(player) == false)
                continue;
            aiChat(player, "Hey "+kbGetPlayerName(player)+", we decided not to support the Treaty mode due to the mechanisms of the mod.");
            aiChat(player, "This AI will not play like how people are supposed to play in a Treaty game. Sorry ^_^ -- AlistairJah");
        }
    }
    if (aiGetGameMode() != cGameModeSupremacy)
    {
        for(player = 1 ; < cNumberPlayers)
        {
            if (kbIsPlayerHuman(player) == false)
                continue;
            aiChat(player, "Hey "+kbGetPlayerName(player)+", this AI hasn't been written to work in any other game mode than Supremacy. Chances are it will have weird behavior.");
            aiChat(player, "I suggest you to cancel this game and set up another one in Supremacy mode ^_^ -- AlistairJah");
        }
    }
    if (kbGetHCLevel(cMyID) < 135)
    {
        for(player = 1 ; < cNumberPlayers)
        {
            if (kbIsPlayerHuman(player) == false)
                continue;
            aiChat(player, "Hey "+kbGetPlayerName(player)+", for the moment, the Maori AI needs a homecity level of 135 to be able to play.");
            aiChat(player, "I'll hopefully be able to fix it in a future patch.");
            aiChat(player, "For now, I advise you to choose a higher level homecity. Sorry for the trouble ^_^ -- AlistairJah");
        }
    }
    if (kbUnitCount(cMyID, cUnitTypeHomeCityWaterSpawnFlag, cUnitStateAlive) >= 1)
    {
        for(player = 1 ; < cNumberPlayers)
        {
            if (kbIsPlayerHuman(player) == false)
                continue;
            aiChat(player, "Hey "+kbGetPlayerName(player)+", I know it's weird but for the moment, the Maori AI doesn't support naval gameplay.");
            aiChat(player, "I'll hopefully be able to fix it in a future patch. That's it, I just wanted you to know that ^_^ -- AlistairJah");
        }
    }
    
    kbAreaCalculate();
    kbLookAtAllUnitsOnMap();
    
    if (aiGetWorldDifficulty() == cDifficultyExpert)
        kbSetPlayerHandicap( cMyID, kbGetPlayerHandicap(cMyID) * 1.5);
    
    kbUnitPickCreate("Unit Picker");
    
    initArrays();
    
    aiSetHandler("HandlerShipmentEarned", cXSShipResourceGranted);
    aiSetHandler("HandlerShipmentArrive", cXSHomeCityTransportArriveHandler);
    aiSetHandler("HandlerShipmentReturn", cXSHomeCityTransportReturnHandler);
    
    kbEscrowSetPercentage(cRootEscrowID, cAllResources, 1.0);
    kbEscrowSetPercentage(cEconomyEscrowID, cAllResources, 0.0);
    kbEscrowSetPercentage(cMilitaryEscrowID, cAllResources, 0.0);
    kbEscrowAllocateCurrentResources();
    
    aiCheatAddResource("Wood", 300);
    int firstPaPorter = findUnit1(cUnitTypePaPorter);
    vector firstPaPorterLoc = kbUnitGetPosition(firstPaPorter);
    int firstPaPlan = planBuild(cUnitTypeMaoriPa, kbBaseGetMainID(cMyID), cUnitTypePaPorter, 1, 100, cRootEscrowID);
    aiPlanSetEventHandler(firstPaPlan, cPlanEventStateChange, "HandlerFirstPaPlanState");
}


void HandlerFirstPaPlanState(int plan = -1)
{
    int pa = findUnit1(cUnitTypeMaoriPa);
    if (kbUnitCount(cMyID, cUnitTypeMaoriPa, cUnitStateAlive) == 0)
        return;
    
    kbBaseDestroyAll(cMyID);
    
    vector paLoc = kbUnitGetPosition(pa);
    vector baseFront = xsVectorNormalize(kbGetMapCenter() - paLoc);
    float dist = 40.0;
    while(kbAreaGroupGetIDByPosition(paLoc + baseFront * dist) != kbAreaGroupGetIDByPosition(paLoc))
    {
        dist = dist - 5.0;
        if (dist < 6.0) break;
    }
    
    int mainBase = kbBaseCreate(cMyID, "MainBase", kbUnitGetPosition(pa), 80.0);
    kbBaseSetMain(cMyID, mainBase, true);
    kbBaseSetEconomy(cMyID, mainBase, true);
    kbBaseSetMilitary(cMyID, mainBase, true);
    kbBaseSetSettlement(cMyID, mainBase, true);
    kbBaseSetFrontVector(cMyID, mainBase, baseFront);
    kbBaseSetMilitaryGatherPoint(cMyID, mainBase, paLoc + baseFront * dist);
    kbBaseSetMaximumResourceDistance(cMyID, mainBase, 150.0);
    kbBaseSetActive(cMyID, mainBase, true);
    
    xsEnableRule("MonitorVillagerPopulation");
    xsEnableRule("MonitorSettlement");
    xsEnableRule("MonitorResources");
    xsEnableRule("MonitorMilitaryPopulation");
    xsEnableRule("MonitorEconomicTechnologies");
    xsEnableRule("MonitorMilitaryTechnologies");
    xsEnableRule("MonitorBigButtonTechnologies");
    xsEnableRule("MonitorOffensiveOperations");
    xsEnableRule("MonitorCommercialTradingPosts");
    xsEnableRule("MonitorNativeTradingPosts");
    xsEnableRule("MonitorReligion");
}


void initArrays(void)
{
    gArrayTier1Treasures = xsArrayCreateInt(20, -1, "Easy Treasures");
    gArrayTier2Treasures = xsArrayCreateInt(20, -1, "Medium Treasures");
    gArrayTier3Treasures = xsArrayCreateInt(20, -1, "Hard Treasures");
    gArrayTier1TreasuresHP = xsArrayCreateFloat(20, 0.0, "Easy Treasures HP");
    gArrayTier2TreasuresHP = xsArrayCreateFloat(20, 0.0, "Medium Treasures HP");
    gArrayTier3TreasuresHP = xsArrayCreateFloat(20, 0.0, "Hard Treasures HP");
    
    int iTier1 = 0;
    int iTier2 = 0;
    int iTier3 = 0;
    
    for(iNugget = 0 ; < kbUnitCount(0, cUnitTypeAbstractNuggetLand, cUnitStateAlive))
    {
        int treasure = findUnitByLocation1(cUnitTypeAbstractNuggetLand, 0, kbUnitGetPosition(findUnit1(cUnitTypeRangatira)), kbGetMapXSize(), iNugget);
        vector treasureLoc = kbUnitGetPosition(treasure);
        int treasureGuards = countUnitsByLocation(cUnitTypeGuardian, 0, treasureLoc, 20.0);
        float hitpoints = 0.0;
        for(iGuard = 0 ; < treasureGuards)
        {
            int guardian = findUnit2(cUnitTypeGuardian, 0, iGuard);
            hitpoints = hitpoints + kbUnitGetCurrentHitpoints(guardian);
        }
        if (hitpoints <= 400.0)
        {
            if (iTier1 >= xsArrayGetSize(gArrayTier1Treasures))
                continue;
            xsArraySetInt(gArrayTier1Treasures, iTier1, treasure);
            xsArraySetFloat(gArrayTier1TreasuresHP, iTier1, hitpoints);
            iTier1++;
        }
        else if ((hitpoints > 400.0) && (hitpoints <= 600.0))
        {
            if (iTier2 >= xsArrayGetSize(gArrayTier2Treasures))
                continue;
            xsArraySetInt(gArrayTier2Treasures, iTier2, treasure);
            xsArraySetFloat(gArrayTier2TreasuresHP, iTier2, hitpoints);
            iTier2++;
        }
        else if (hitpoints > 600.0)
        {
            if (iTier3 >= xsArrayGetSize(gArrayTier3Treasures))
                continue;
            xsArraySetInt(gArrayTier3Treasures, iTier3, treasure);
            xsArraySetFloat(gArrayTier3TreasuresHP, iTier3, hitpoints);
            iTier3++;
        }
    }
}


bool blockaded = false;
int orderedCard = -1;
void orderCard(int cardIndex = -1)
{
    orderedCard = cardIndex;
    aiHCDeckPlayCard(cardIndex);
}

bool HandlerShipmentShipMilitary(void)
{
    static int counter = 0;
    if (counter >= 6) return(false);
    
    for(iDeckCard = 0 ; < aiHCDeckGetNumberCards())
    {
        if (aiHCDeckCanPlayCard(iDeckCard) == false)
            continue;
        if (aiHCDeckGetCardTechID(cDeckID, iDeckCard) == cTechHCCardNgapuhiForce)
        {
            orderCard(iDeckCard);
            counter++;
            return(true);
        }
    }
    for(iDeckCard = 0 ; < aiHCDeckGetNumberCards())
    {
        if (aiHCDeckCanPlayCard(iDeckCard) == false)
            continue;
        if (aiHCDeckGetCardTechID(cDeckID, iDeckCard) == cTechHCCard10Marksmen)
        {
            orderCard(iDeckCard);
            counter++;
            return(true);
        }
    }
    for(iDeckCard = 0 ; < aiHCDeckGetNumberCards())
    {
        if (aiHCDeckCanPlayCard(iDeckCard) == false)
            continue;
        if (aiHCDeckGetCardTechID(cDeckID, iDeckCard) == cTechHCCard10Spearmen)
        {
            orderCard(iDeckCard);
            counter++;
            return(true);
        }
    }
    for(iDeckCard = 0 ; < aiHCDeckGetNumberCards())
    {
        if (aiHCDeckCanPlayCard(iDeckCard) == false)
            continue;
        if (aiHCDeckGetCardTechID(cDeckID, iDeckCard) == cTechHCCard9Fighters)
        {
            orderCard(iDeckCard);
            counter++;
            return(true);
        }
    }
    for(iDeckCard = 0 ; < aiHCDeckGetNumberCards())
    {
        if (aiHCDeckCanPlayCard(iDeckCard) == false)
            continue;
        if (aiHCDeckGetCardTechID(cDeckID, iDeckCard) == cTechHCCard6Marksmen)
        {
            orderCard(iDeckCard);
            counter++;
            return(true);
        }
    }
    for(iDeckCard = 0 ; < aiHCDeckGetNumberCards())
    {
        if (aiHCDeckCanPlayCard(iDeckCard) == false)
            continue;
        if (aiHCDeckGetCardTechID(cDeckID, iDeckCard) == cTechHCCard6Spearmen)
        {
            orderCard(iDeckCard);
            counter++;
            return(true);
        }
    }
    return(false);
}


void HandlerShipmentEarned(int param = -1)
{
    if (blockaded) return;
    if (kbResourceGet(cResourceShips) == 0) return;
    static int needanothership = -1;
    
    if (needanothership >= 0)
    {
        if (kbResourceGet() <= 1)
            return;
        orderCard(needanothership);
        needanothership = -1;
    }
    
    if (kbUnitCount(cMyID, cUnitTypeLogicalTypeLandMilitary, cUnitStateABQ) <= 7)
    {
        if (HandlerShipmentShipMilitary()) return;
    }
    
    float bestScore = 0.0;
    int bestCard = -1;
    for(iDeckCard = 0 ; < aiHCDeckGetNumberCards())
    {
        if (aiHCDeckCanPlayCard(iDeckCard) == false)
            continue;
        
        float score = 1.0;
        if (aiHCDeckGetCardTechID(cDeckID, iDeckCard) == cTechHCCard3PolyVillagers)
            score = 10.0;
        if (aiHCDeckGetCardTechID(cDeckID, iDeckCard) == cTechHCCard10RabbitsAnd9Villagers)
            score = 9.0;
        if (aiHCDeckGetCardTechID(cDeckID, iDeckCard) == cTechHCCard5WhiteGumAnd6Villagers)
            score = 8.0;
        if (aiHCDeckGetCardTechID(cDeckID, iDeckCard) == cTechHCCard5KiwiAnd4Villagers)
            score = 7.0;
        
        if (bestScore < score)
        {
            bestScore = score;
            bestCard = iDeckCard;
        }
    }
    
    if (bestCard >= 0)
    {
        if (bestScore >= 7.0)
        {
            if (aiHCDeckGetCardTechID(cDeckID, bestCard) == cTechHCCard3PolyVillagers)
            {
                needanothership = -1;
                orderCard(bestCard);
                return;
            }
            if (kbResourceGet(cResourceShips) <= 1)
            {
                needanothership = bestCard;
                return;
            }
            orderCard(bestCard);
        }
    }
}


void HandlerShipmentArrive(int param = -1)
{
    orderedCard = -1;
}


void HandlerShipmentReturn(int param = -1)
{
    blockaded = true;
    xsDisableRule("MonitorSpareShipments");
    for(player = 1 ; < cNumberPlayers)
    {
        if (player == cMyID) continue;
        if (kbIsPlayerEnemy(player)) continue;
        aiChat(player, "Looks like we've been blockaded lol");
        if (aiRandInt(100) < 50)
            aiChat(player, "I ordered the card "+kbGetTechName(aiHCDeckGetCardTechID(orderedCard))+" but it couldn't be shipped :(");
    }
}


rule MonitorHomecity active minInterval 1 group StartupMonitors
{
    xsDisableSelf();
    for(attempt = 0 ; < 6)
    {
        for(index = 0 ; < aiHCCardsGetTotal())
            aiHCCardsBuyCard(index);
    }
    xsEnableRule("MonitorDeck");
}


rule MonitorDeck inactive minInterval 1
{
    xsDisableSelf();
    for(iHCCard = 0 ; < aiHCCardsGetTotal())
    {
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCard5KiwiAnd4Villagers)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCard5WhiteGumAnd6Villagers)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCardGeologicalSurvey)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCard10Spearmen)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCard10Marksmen)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCardTEAMPounamu)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCardFlagstaffWar)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCardBattlefieldConstructionPOLY)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCardInvasionOfWaikato)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCardFirePerformance)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCardOldWaysMAO)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCardHui)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCardRatanaPa)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCard6Marksmen)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCard6Spearmen)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCardMauRakau)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCard9Fighters)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCard3PolyVillagers)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCardMonoiOil)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCard10RabbitsAnd9Villagers)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCardLandWars)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
        if (aiHCCardsGetCardTechID(iHCCard) == cTechHCCardNgapuhiForce)
            aiHCDeckAddCardToDeck(cDeckID, iHCCard);
    }
    xsEnableRule("MonitorSpareShipments");
}


rule MonitorSpareShipments inactive minInterval 5
{
    HandlerShipmentEarned();
}


rule MonitorExploration active minInterval 1 group StartupMonitors
{
    xsEnableRule("MonitorRansoms");
    
    if (kbGetAge() >= cAge2)
    {
        if (aiPlanGetNumber(cPlanExplore, -1, true) <= 4)
        {
            int exploreplan = aiPlanCreate("Explore", cPlanExplore);
            aiPlanSetDesiredPriority(exploreplan, 100);
            aiPlanAddUnitType(exploreplan, cUnitTypeLogicalTypeValidSharpshoot, 1, 1, 1);
            aiPlanSetActive(exploreplan, true);
        }
    }
    
    if (kbGetAge() >= cAge4)
    {
        gRangatiraDefendMode = true; // TODO
        xsDisableRule("MonitorTreasures");
        xsDisableSelf();
        // TODO: Clear all exploration-themed static/global variables
        return;
    }
    
    int rangatira = findUnit1(cUnitTypeRangatira);
    
    if ((kbUnitGetPlanID(rangatira) >= 0) || (rangatira == -1))
    {
        gTreasureHuntingMode = false;
        gRangatiraRecoveryMode = true;
        return;
    }
    
    if (gTreasureHuntingMode == true)
        return;
    if (gRangatiraRecoveryMode == true)
    {
        if (kbUnitGetHealth(rangatira) < .8)
            return;
        gRangatiraRecoveryMode = false;
    }
    
    static int timer = -1;
    static int mode = 0;
    timer++;
    if (timer >= 15)
    {
        timer = 0;
        mode = abs(mode) - 1;
    }
    
    static int tier = 1;
    int array = gArrayTier3Treasures;
    int arrayHP = gArrayTier3TreasuresHP;
    if (tier == 1)
    {
        array = gArrayTier1Treasures;
        arrayHP = gArrayTier1TreasuresHP;
    }
    if (tier == 2)
    {
        array = gArrayTier2Treasures;
        arrayHP = gArrayTier2TreasuresHP;
    }
    bool found = false;
    float min = 999999;
    int bestTreasure = -1;
    
    for(iTreasure = 0 ; < xsArrayGetSize(array))
    {
        if ((tier >= 2) && (kbGetAge() == cAge1))
            break;
        // TODO: top tier treasures if ((tier >= 3) && (kbGetAge() <= cAge3))
        if (tier >= 3)
            break;
        float hitpoints = xsArrayGetFloat(arrayHP, iTreasure);
        if (hitpoints < 0.0)
            continue;
        if (hitpoints < min)
        {
            int treasure = xsArrayGetInt(array, iTreasure);
            if (treasure == -1)
            {
                xsArraySetFloat(array, iTreasure, -1.0);
                continue;
            }
            if (kbUnitGetCurrentHitpoints(treasure) == 0)
            {
                xsArraySetInt(array, iTreasure, -1);
                xsArraySetFloat(arrayHP, iTreasure, -1.0);
                continue;
            }
            min = hitpoints;
            bestTreasure = treasure;
            found = true;
        }
    }
    
    if (found)
    {
        gTreasureHuntingMode = true;
        gTreasure = bestTreasure;
        gTreasureTier = tier;
        xsEnableRule("MonitorTreasures");
        return;
    }
    
    tier++;
    if (tier >= 3)
        tier = 3;
    
    for(iHerd = 0 ; < kbUnitCount(0, cUnitTypeHerdable, cUnitStateAlive))
    {
        int herdable = findUnitByLocation1(cUnitTypeHerdable, 0, kbUnitGetPosition(rangatira), 500.0, iHerd);
        if (kbAreaGroupGetIDByPosition(kbUnitGetPosition(herdable)) != kbAreaGroupGetIDByPosition(kbUnitGetPosition(rangatira)))
            continue;
        int herdArea = kbUnitGetAreaID(herdable);
        float tiles = kbAreaGetNumberTiles(herdArea);
        float black = kbAreaGetNumberFogTiles(herdArea);
        if (black / tiles < .7)
        {
            aiTaskUnitMove(rangatira, kbUnitGetPosition(herdable));
            return;
        }
    }
    
    if ((mode == -1) && (timer % 10 == 0))
    {
        aiTaskUnitMove(rangatira, aiRandLocation());
    }
    else if ((mode == 0) && (timer % 10 == 0))
    {
        for(iArea = 0 ; < kbAreaGetNumber())
        {
            vector loc = kbAreaGetCenter(iArea);
            if (xsVectorLength(loc - kbGetMapCenter()) > kbGetMapXSize() * 0.5)
                continue;
            if (kbAreaGroupGetIDByPosition(loc) != kbAreaGroupGetIDByPosition(kbUnitGetPosition(rangatira)))
                continue;
            tiles = kbAreaGetNumberTiles(iArea);
            black = kbAreaGetNumberBlackTiles(iArea);
            if (black / tiles <= .35)
            {
                continue;
            }
            else
            {
                if (aiRandInt(100) < 50)
                {
                    aiTaskUnitMove(rangatira, aiRandLocation());
                    continue;
                }
                aiTaskUnitMove(rangatira, loc);
                break;
            }
        }
    }
}


rule MonitorTreasures inactive minInterval 1 runImmediately
{
    static bool pick = true;
    int rangatira = findUnit1(cUnitTypeRangatira);
    
    if ((kbUnitGetPlanID(rangatira) >= 0) || (rangatira == -1))
    {
        gTreasureHuntingMode = false;
        gRangatiraRecoveryMode = true;
        return;
    }
    
    if (kbUnitGetHealth(rangatira) < .4)
    {
        pick = true;
        gTreasureHuntingMode = false;
        aiTaskUnitMove(rangatira, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)));
        gRangatiraRecoveryMode = true;
        xsDisableSelf();
        return;
    }
    
    vector treasureLoc = kbUnitGetPosition(gTreasure);
    
    if ((kbUnitGetProtoUnitID(gTreasure) == cUnitTypeNuggetBearTree) || 
        (kbUnitGetProtoUnitID(gTreasure) == cUnitTypeNuggetKidnap) || 
        (kbUnitGetProtoUnitID(gTreasure) == cUnitTypeNuggetKidnapBrit) || 
        (kbUnitGetProtoUnitID(gTreasure) == cUnitTypeNuggetPirate) || 
        (kbUnitGetProtoUnitID(gTreasure) == cUnitTypeNuggetWolfMissionary) || 
        (kbUnitGetProtoUnitID(gTreasure) == cUnitTypeNuggetWolfRock) || 
        (kbUnitGetProtoUnitID(gTreasure) == cUnitTypeNuggetWolfTreebent) || 
        (kbUnitGetProtoUnitID(gTreasure) == cUnitTypeypNuggetKidnapAsian) || 
        (kbUnitGetProtoUnitID(gTreasure) == cUnitTypeypNuggetPirateAsian) || 
        (kbUnitGetProtoUnitID(gTreasure) == cUnitTypeypNuggetTreeAsian))
    {
        if (countUnitsByLocation(cUnitTypeConvertsHerds, 0, treasureLoc, 20.0) == 0)
        {
            int array = gArrayTier3Treasures;
            int arrayHP = gArrayTier3TreasuresHP;
            if (gTreasureTier == 1)
            {
                array = gArrayTier1Treasures;
                arrayHP = gArrayTier1TreasuresHP;
            }
            if (gTreasureTier == 2)
            {
                array = gArrayTier2Treasures;
                arrayHP = gArrayTier2TreasuresHP;
            }
            for(index = 0 ; < xsArrayGetSize(array))
            {
                if (gTreasure == xsArrayGetInt(array, index))
                {
                    xsArraySetInt(array, index, -1);
                    xsArraySetFloat(arrayHP, index, -1.0);
                    pick = true;
                    gTreasureHuntingMode = false;
                    xsDisableSelf();
                    return;
                }
            }
        }
    }
    else
    {
        if (kbUnitGetCurrentHitpoints(gTreasure) == 0.0)
        {
            array = gArrayTier3Treasures;
            arrayHP = gArrayTier3TreasuresHP;
            if (gTreasureTier == 1)
            {
                array = gArrayTier1Treasures;
                arrayHP = gArrayTier1TreasuresHP;
            }
            if (gTreasureTier == 2)
            {
                array = gArrayTier2Treasures;
                arrayHP = gArrayTier2TreasuresHP;
            }
            for(index = 0 ; < xsArrayGetSize(array))
            {
                if (gTreasure == xsArrayGetInt(array, index))
                {
                    xsArraySetInt(array, index, -1);
                    xsArraySetFloat(arrayHP, index, -1.0);
                    pick = true;
                    gTreasureHuntingMode = false;
                    xsDisableSelf();
                    return;
                }
            }
        }
    }
    
    if (pick)
    {
        int guardian = findUnitByLocation1(cUnitTypeGuardian, 0, treasureLoc, 20.0);
        aiTaskUnitWork(rangatira, guardian);
        pick = false;
    }
    
    int treasureGuards = countUnitsByLocation(cUnitTypeGuardian, 0, treasureLoc, 20.0);
    float hitpoints = 0.0;
    for(iGuard = 0 ; < treasureGuards)
    {
        guardian = findUnit1(cUnitTypeGuardian, 0, iGuard);
        hitpoints = hitpoints + kbUnitGetCurrentHitpoints(guardian);
    }
    if (hitpoints > 0.0)
        return;
    
    aiTaskUnitWork(rangatira, gTreasure);
}


rule MonitorRansoms inactive minInterval 5 runImmediately
{
    if (kbGetAge() >= cAge5)
        return; // He's only useful for voyages... TODO for now
    if (kbUnitCount(cMyID, cUnitTypeMaoriPa, cUnitStateAlive) == 0)
        return;
    if (kbResourceGet(cResourceGold) < 100)
        return;
    
    aiRansomExplorer(aiGetFallenExplorerID(), cRootEscrowID, findUnit1(cUnitTypeMaoriPa));
}


rule MonitorHerdables inactive minInterval 1
{
    const float autoconvertrange = 16.0;
    const int cLivestockLimit = 10;
    
    int numherds = kbUnitCount(cMyID, cUnitTypeHerdable, cUnitStateABQ);
    int numpen = 0;
    if (numherds >= 4)
    {
        numpen = numherds / cLivestockLimit;
        if (numpen <= 0)
            numpen = 1;
    }
    int numpentomake = numpen - kbUnitCount(cMyID, cUnitTypeLivestockPen, cUnitStateABQ);
    if ((numpentomake >= 1) && (aiPlanGetIDByTypeAndVariableType(cPlanBuild, cBuildPlanBuildingTypeID, cUnitTypeLivestockPen, true) <= -1))
    {
        int plan = planBuild(cUnitTypeLivestockPen, kbBaseGetMainID(cMyID), cUnitTypePOLYvillager);
        aiPlanSetEventHandler(plan, cPlanEventStateChange, "HandlerSettlementConstructionState");
    }
    
    for(iHerd = 0 ; < numherds)
    {
        int herd = findUnitByLocation1(cUnitTypeHerdable, cMyID, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)), 500.0, iHerd);
        vector herdLoc = kbUnitGetPosition(herd);
        if (kbUnitIsInventoryFull(herd) == true)
        {
            int bldg = findUnitByLocation2(cUnitTypeLogicalTypeBuildingsNotWallsOrGroves, cMyID, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)), 40.0);
            vector center = kbUnitGetPosition(bldg);
            aiTaskUnitMove(herd, center + xsVectorNormalize(herdLoc - center) * (autoconvertrange - 6.0));
            continue;
        }
        if (kbUnitGetTargetUnitID(herd) >= 0)
            continue;
        if (kbUnitGetActionType(herd) == 9)
            continue;
        // if (countUnitsByLocation(cUnitTypeConvertsHerds, cMyID, herdLoc, autoconvertrange - 6.0) >= 1)
            // continue;
        int herdAreaGroup = kbAreaGroupGetIDByPosition(herdLoc);
        for(iPen = 0 ; < kbUnitCount(cMyID, cUnitTypeLivestockPen, cUnitStateAlive))
        {
            int pen = findUnitByLocation2(cUnitTypeLivestockPen, cMyID, herdLoc, 500.0, iPen);
            if (kbUnitGetNumberWorkers(pen) >= cLivestockLimit)
                continue;
            vector penLoc = kbUnitGetPosition(pen);
            int penAreaGroup = kbAreaGroupGetIDByPosition(penLoc);
            if (penAreaGroup != herdAreaGroup)
                continue;
            aiTaskUnitWork(herd, pen);
            break;
        }
        for(iPen = 0 ; < kbUnitCount(cMyID, cUnitTypeLogicalTypeBuildingsNotWalls, cUnitStateAlive))
        {
            pen = findUnitByLocation2(cUnitTypeLogicalTypeBuildingsNotWalls, cMyID, herdLoc, 500.0, iPen);
            penLoc = kbUnitGetPosition(pen);
            penAreaGroup = kbAreaGroupGetIDByPosition(penLoc);
            if (penAreaGroup != herdAreaGroup)
                continue;
            aiTaskUnitMove(herd, penLoc);
            break;
        }
    }
}


rule MonitorGathererAllocation active minInterval 30 runImmediately group StartupMonitors
{
    clearForecasts();
    
    const int kumaraGathererLimit = 10;
    int need = 1 + ((kbUnitCount(cMyID, cUnitTypeAbstractVillager, cUnitStateAlive) * 0.5) / kumaraGathererLimit);  // Enough for 50% of population
    need = need - kbUnitCount(cMyID, cUnitTypeKumaraField, cUnitStateABQ);
    if (gFarmingMode)
        addItemToForecasts(cUnitTypeKumaraField, need);
    need = kbGetBuildLimit(cMyID, cUnitTypeSandalwoodGrove) - kbUnitCount(cMyID, cUnitTypeSandalwoodGrove, cUnitStateABQ);
    if (kbProtoUnitAvailable(cUnitTypeSandalwoodGrove))
        addItemToForecasts(cUnitTypeSandalwoodGrove, need);
    need = kbGetBuildLimit(cMyID, cUnitTypeMaoriPa) - kbUnitCount(cMyID, cUnitTypeMaoriPa, cUnitStateABQ);
    addItemToForecasts(cUnitTypeMaoriPa, need);
    need = 1 - kbUnitCount(cMyID, cUnitTypeMarket, cUnitStateABQ);
    addItemToForecasts(cUnitTypeMarket, need);
    
    addItemToForecasts(cUnitTypePOLYvillager, 8);
    
    addTechToForecasts(cTechBigBountifulIslandsMaori, true);
    addTechToForecasts(cTechWhanau);
    addTechToForecasts(cTechKumaraStorage);
    addTechToForecasts(cTechHapu);
    addTechToForecasts(cTechHangi);
    addTechToForecasts(cTechIwi);
    addTechToForecasts(cTechBirdSnares);
    addTechToForecasts(cTechStoneAdzes);
    addTechToForecasts(cTechBasaltQuarry);
    addTechToForecasts(cTechBarkCloth, true);
    addTechToForecasts(cTechRatSnares, true);
    addTechToForecasts(cTechEuropeanAxes, true);
    addTechToForecasts(cTechPetroglyphs, true);
    addTechToForecasts(cTechTattooing, true);
    addTechToForecasts(cTechWoodCarving, true);
    addTechToForecasts(cTechDryStoneConstruction, true);
    addTechToForecasts(cTechTradeMission, true);
    addTechToForecasts(cTechSandalwoodTrade, true);
    addTechToForecasts(cTechWovenBaskets);
    addTechToForecasts(cTechPohaKelpBags, true);
    addTechToForecasts(cTechPallisades);
    addTechToForecasts(cTechFightingPlatforms);
    addTechToForecasts(cTechPakehaCannons);
    
    if (kbGetAge() >= cAge2)
    {
        addItemToForecasts(cUnitTypePOLYMarauder, 10);
        addItemToForecasts(cUnitTypePOLYSpearman, 10);
        addItemToForecasts(cUnitTypePolyStoneThrower, 5);
        addTechToForecasts(cTechTuparaMarauders);
        addTechToForecasts(cTechStrongTuparaMarauders);
        addTechToForecasts(cTechExpertTuparaMarauders);
        addTechToForecasts(cTechTewhatewhaSpearmen);
        addTechToForecasts(cTechStrongTewhatewhaSpearmen);
        addTechToForecasts(cTechExpertTewhatewhaSpearmen);
    }
    
    switch(kbGetAge())
    {
        case cAge1:
        {
            if (gAgingUp == false)
                addItemToForecasts(cUnitTypePOLYVMFiji2, 2);
            break;
        }
        case cAge2:
        {
            if (gAgingUp == false)
                addItemToForecasts(cUnitTypePOLYVMVanDiemensLand3, 2);
            need = 1 - kbUnitCount(cMyID, cUnitTypePOLYTrainingGround, cUnitStateABQ);
            addItemToForecasts(cUnitTypePOLYTrainingGround, need);
            break;
        }
        case cAge3:
        {
            if (gAgingUp == false)
                addItemToForecasts(cUnitTypePOLYVMChathamIslands4, 1);
            need = kbGetBuildLimit(cMyID, cUnitTypePOLYTemple) - kbUnitCount(cMyID, cUnitTypePOLYTemple, cUnitStateABQ);
            addItemToForecasts(cUnitTypePOLYTemple, need);
            need = 2 - kbUnitCount(cMyID, cUnitTypePOLYTrainingGround, cUnitStateABQ);
            addItemToForecasts(cUnitTypePOLYTrainingGround, need);
            need = 1 - kbUnitCount(cMyID, cUnitTypePOLYSiegeWorkshop, cUnitStateABQ);
            addItemToForecasts(cUnitTypePOLYSiegeWorkshop, need);
            break;
        }
        case cAge4:
        {
            if (gAgingUp == false)
                addItemToForecasts(cUnitTypePOLYVMNewSouthWales5, 1);
            need = 3 - kbUnitCount(cMyID, cUnitTypePOLYTrainingGround, cUnitStateABQ);
            addItemToForecasts(cUnitTypePOLYTrainingGround, need);
            need = 2 - kbUnitCount(cMyID, cUnitTypePOLYSiegeWorkshop, cUnitStateABQ);
            addItemToForecasts(cUnitTypePOLYSiegeWorkshop, need);
            break;
        }
        case cAge5:
        {
            need = 4 - kbUnitCount(cMyID, cUnitTypePOLYTrainingGround, cUnitStateABQ);
            addItemToForecasts(cUnitTypePOLYTrainingGround, need);
            need = 3 - kbUnitCount(cMyID, cUnitTypePOLYSiegeWorkshop, cUnitStateABQ);
            addItemToForecasts(cUnitTypePOLYSiegeWorkshop, need);
            break;
        }
        default:
        {
            
            break;
        }
    }
    
    if (kbGetAge() == cAge1)
    {
        if (gAgingUp == false)
        {
            clearForecasts();
            addResourceToForecasts(cResourceFood, 1000.0);
        }
        else
        {
            addResourceToForecasts(cResourceWood, 1500.0);
        }
    }
    
    aiSetResourceGathererPercentageWeight(cRGPScript, 1.0);
    aiSetResourceGathererPercentageWeight(cRGPCost, 0.0);
    
    float forecastWeight = 1.0;
    float reactiveWeight = 0.0;
    static int forecastValues = -1;
    static int reactiveValues = -1;
    static int gathererPercentages = -1;
    
    if (forecastValues < 0)
    {
        forecastValues = xsArrayCreateFloat(cNumResourceTypes, 0.0, "forecast oriented values");
        reactiveValues = xsArrayCreateFloat(cNumResourceTypes, 0.0, "reactive values");
        gathererPercentages = xsArrayCreateFloat(cNumResourceTypes, 0.0, "gatherer percentages");
    }
    
    float totalForecast = 0.0;
    float totalShortfall = 0.0;
    float fcst = 0.0;
    float shortfall = 0.0;
    for (i=0; <cNumResourceTypes)
    {
        fcst = xsArrayGetFloat(gForecasts, i);
        shortfall = fcst - kbResourceGet(i);
        totalForecast = totalForecast + fcst;
        if (shortfall > 0.0)
            totalShortfall = totalShortfall + shortfall;
    }
    
    if (totalForecast > 0)
        reactiveWeight = totalShortfall / totalForecast;
    else
        reactiveWeight = 1.0;
    forecastWeight = 1.0 - reactiveWeight;
    if (totalShortfall > (0.3 * totalForecast))
    {
        reactiveWeight = reactiveWeight + (0.7 * forecastWeight);
        forecastWeight = 1.0 - reactiveWeight;
    }
    
    float scratch = 0.0;
    for (i=0; <cNumResourceTypes)
    {
        fcst = xsArrayGetFloat(gForecasts, i);
        shortfall = fcst - kbResourceGet(i);
        xsArraySetFloat(forecastValues, i, fcst / totalForecast);
        if ( shortfall > 0 )
            xsArraySetFloat(reactiveValues, i, shortfall / totalShortfall);
        else
            xsArraySetFloat(reactiveValues, i, 0.0);
        
        scratch = xsArrayGetFloat(forecastValues, i) * forecastWeight;
        scratch = scratch + (xsArrayGetFloat(reactiveValues, i) * reactiveWeight);
        xsArraySetFloat(gathererPercentages, i, scratch);
    }
    
    float totalPercentages = 0.0;
    for(i=0; <cNumResourceTypes)
        totalPercentages = totalPercentages + xsArrayGetFloat(gathererPercentages, i);
    for(i=0; <cNumResourceTypes)
        xsArraySetFloat(gathererPercentages, i, xsArrayGetFloat(gathererPercentages, i) / totalPercentages);
    
    if (countUnitsByLocation(cUnitTypeTree, 0, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)), 130.0) < 30)
        xsArraySetFloat(gathererPercentages, cResourceWood, 0);
    
    totalPercentages = 0.0;
    for(i=0; <cNumResourceTypes)
        totalPercentages = totalPercentages + xsArrayGetFloat(gathererPercentages, i);
    for(i=0; <cNumResourceTypes)
        xsArraySetFloat(gathererPercentages, i, xsArrayGetFloat(gathererPercentages, i) / totalPercentages);
    
    for (i=0; <cNumResourceTypes)
        aiSetResourceGathererPercentage(i, xsArrayGetFloat(gathererPercentages, i), false, cRGPScript);
    
    aiNormalizeResourceGathererPercentages(cRGPScript);
    
    xsEnableRule("MonitorGathererTasking");
}


rule MonitorGathererTasking inactive minInterval 5 runImmediately
{
    int numVills = kbUnitCount(cMyID, cUnitTypeAbstractVillager);
    int foodGatherers = numVills * aiGetResourceGathererPercentage(cResourceFood, cRGPScript);
    int woodGatherers = numVills * aiGetResourceGathererPercentage(cResourceWood, cRGPScript);
    int goldGatherers = numVills - foodGatherers - woodGatherers;
    int crteGatherers = 3;
    int prayers = 0;
    if (gWeAreReligious) prayers = 10;
    static int resourceQuery = -1;
    if (resourceQuery == -1)
    {
        resourceQuery = kbUnitQueryCreate("AllResources");
        kbUnitQuerySetPlayerRelation(resourceQuery, cPlayerRelationAny);
        kbUnitQuerySetState(resourceQuery, cUnitStateAny);
        kbUnitQuerySetIgnoreKnockedOutUnits(resourceQuery, true);
        kbUnitQuerySetPlayerID(resourceQuery, -1, false);
        kbUnitQuerySetPosition(resourceQuery, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)));
        // kbUnitQuerySetMaximumDistance(resourceQuery, kbBaseGetMaximumResourceDistance(cMyID, kbBaseGetMainID(cMyID)));
        kbUnitQuerySetMaximumDistance(resourceQuery, 300.0);
        kbUnitQuerySetUnitType(resourceQuery, cUnitTypeResource);
    }
    if (xsGetTime() > 1200000)
        kbUnitQuerySetState(resourceQuery, cUnitStateAlive);
    kbUnitQueryResetResults(resourceQuery);
    kbUnitQueryExecute(resourceQuery);
    
    for(iVill = 0 ; < kbUnitCount(cMyID,cUnitTypeAbstractVillager))
    {
        int villager = findUnit1(cUnitTypeAbstractVillager, cMyID, iVill);
        if (kbUnitIsType(villager, cUnitTypeHero))
            continue;
        if (kbUnitIsType(villager, cUnitTypeAbstractWagon))
            continue;
        if (kbUnitIsType(villager, cUnitTypeAbstractFishingBoat))
            continue;
        if (kbUnitGetActionType(villager) == 9)
            continue;
        else if (kbUnitGetActionType(villager) == 0)
            continue;
        else if ((kbUnitGetActionType(villager) == 3) || (kbUnitGetActionType(villager) == 6))
        {
            int resource = kbUnitGetTargetUnitID(villager);
            if (kbUnitGetCurrentInventory(resource, cResourceFood) > 0.0)
            {
                if (foodGatherers >= 1)
                {
                    foodGatherers--;
                    continue;
                }
            }
            else if (kbUnitGetCurrentInventory(resource, cResourceWood) > 0.0)
            {
                if (woodGatherers >= 1)
                {
                    woodGatherers--;
                    continue;
                }
            }
            else if (kbUnitGetCurrentInventory(resource, cResourceGold) > 0.0)
            {
                if (goldGatherers >= 1)
                {
                    goldGatherers--;
                    continue;
                }
            }
        }
        
        if (kbUnitIsType(resource, cUnitTypeAbstractResourceCrate) == true)
        {
            crteGatherers--;
            continue;
        }
        
        vector villagerLoc = kbUnitGetPosition(villager);
        if (countUnitsByLocation(cUnitTypeLogicalTypeLandMilitary, cPlayerRelationEnemyNotGaia, villagerLoc, 50.0) > countUnitsByLocation(cUnitTypeLogicalTypeLandMilitary, cPlayerRelationAlly, villagerLoc, 50.0))
        {
            villagerLoc = kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID));
        }
        
        resource = doubleQuery(resourceQuery, cUnitTypeResource, villagerLoc, 300.0, 0);
        for(iResource = 0 ; < kbUnitQueryNumberResults(gDoubleQuery))
        {
            resource = kbUnitQueryGetResult(gDoubleQuery, iResource);
            
            if (kbUnitIsType(resource, cUnitTypeTreeSandalwood))
            {
                if (kbUnitGetPlayerID(resource) != cMyID)
                    continue;
                if (kbUnitGetNumberWorkersIfSeeable(resource) >= 4)
                    continue;
                if (goldGatherers <= 0)
                    continue;
                goldGatherers--;
                aiTaskUnitWork(villager, resource);
                break;
            }
            if (kbUnitIsType(resource, cUnitTypeBuilding))
            {
                if (kbUnitGetPlayerID(resource) != cMyID)
                    continue;
                if (kbUnitGetNumberWorkersIfSeeable(resource) >= 10)
                    continue;
                if (kbUnitGetProtoUnitID(resource) == cUnitTypePOLYTemple)
                {
                    if (prayers <= 0)
                        continue;
                    prayers--;
                    aiTaskUnitWork(villager, resource);
                }
                if (kbUnitGetCurrentInventory(resource, cResourceFood) > 0.0)
                {
                    if (kbGetAmountValidResources(kbBaseGetMainID(cMyID), cResourceFood, cAIResourceSubTypeHunt, 150.0) > 2000.0)
                        continue;
                    if (foodGatherers <= 0)
                        continue;
                    foodGatherers--;
                    aiTaskUnitWork(villager, resource);
                    break;
                }
                if (kbUnitGetCurrentInventory(resource, cResourceWood) > 0.0)
                {
                    if (woodGatherers <= 0)
                        continue;
                    woodGatherers--;
                    aiTaskUnitWork(villager, resource);
                    break;
                }
                if (kbUnitGetCurrentInventory(resource, cResourceGold) > 0.0)
                {
                    if (goldGatherers <= 0)
                        continue;
                    goldGatherers--;
                    aiTaskUnitWork(villager, resource);
                    break;
                }
            }
            if (kbUnitIsType(resource, cUnitTypeAbstractResourceCrate))
            {
                if (kbUnitGetPlayerID(resource) != cMyID)
                {
                    if (kbUnitGetPlayerID(resource) != 0)
                        continue;
                }
                if (kbUnitGetNumberWorkersIfSeeable(resource) >= 3)
                    continue;
                if (crteGatherers <= 0)
                    continue;
                crteGatherers--;
                aiTaskUnitWork(villager, resource);
                break;
            }
            if (kbUnitIsType(resource, cUnitTypeTree))
            {
                if (kbUnitGetNumberWorkersIfSeeable(resource) >= 8)
                    continue;
                if (woodGatherers <= 0)
                    continue;
                woodGatherers--;
                aiTaskUnitWork(villager, resource);
                break;
            }
            if (kbUnitIsType(resource, cUnitTypeAbstractFruit))
            {
                if (kbGetAmountValidResources(kbBaseGetMainID(cMyID), cResourceFood, cAIResourceSubTypeHunt, 150.0) > 2000.0)
                    continue;
                if (kbUnitGetNumberWorkersIfSeeable(resource) >= 8)
                    continue;
                if (kbUnitGetCurrentInventory(resource, cResourceFood) > 0.0)
                {
                    if (foodGatherers <= 0)
                        continue;
                    foodGatherers--;
                    aiTaskUnitWork(villager, resource);
                    break;
                }
                if (kbUnitGetCurrentInventory(resource, cResourceWood) > 0.0)
                {
                    if (woodGatherers <= 0)
                        continue;
                    woodGatherers--;
                    aiTaskUnitWork(villager, resource);
                    break;
                }
                if (kbUnitGetCurrentInventory(resource, cResourceGold) > 0.0)
                {
                    if (goldGatherers <= 0)
                        continue;
                    goldGatherers--;
                    aiTaskUnitWork(villager, resource);
                    break;
                }
            }
            if (kbUnitIsType(resource, cUnitTypeMinedResource))
            {
                if (kbUnitGetNumberWorkersIfSeeable(resource) >= 20)
                    continue;
                if (goldGatherers <= 0)
                    continue;
                goldGatherers--;
                aiTaskUnitWork(villager, resource);
                break;
            }
            if (kbUnitIsType(resource, cUnitTypeHerdable))
            {
                if (kbUnitGetPlayerID(resource) != cMyID)
                {
                    if (kbUnitGetPlayerID(resource) != 0)
                        continue;
                }
                if (kbUnitIsInventoryFull(resource) == false)
                    continue;
                if (countUnitsByLocation(cUnitTypeAbstractShrine, cPlayerRelationAny, kbUnitGetPosition(resource), 16.0) >= 1)
                    continue;
                if (kbUnitGetNumberWorkersIfSeeable(resource) >= 8)
                    continue;
                if (kbUnitGetCurrentInventory(resource, cResourceFood) > 0.0)
                {
                    if (foodGatherers <= 0)
                        continue;
                    foodGatherers--;
                    aiTaskUnitWork(villager, resource);
                    break;
                }
                if (kbUnitGetCurrentInventory(resource, cResourceWood) > 0.0)
                {
                    if (woodGatherers <= 0)
                        continue;
                    woodGatherers--;
                    aiTaskUnitWork(villager, resource);
                    break;
                }
                if (kbUnitGetCurrentInventory(resource, cResourceGold) > 0.0)
                {
                    if (goldGatherers <= 0)
                        continue;
                    goldGatherers--;
                    aiTaskUnitWork(villager, resource);
                    break;
                }
            }
            if (kbUnitIsType(resource, cUnitTypeAnimalPrey))
            {
                if (countUnitsByLocation(cUnitTypeAbstractShrine, cPlayerRelationAny, kbUnitGetPosition(resource), 16.0) >= 1)
                    continue;
                if (kbUnitGetNumberWorkersIfSeeable(resource) >= 8)
                    continue;
                if (kbUnitGetCurrentInventory(resource, cResourceFood) > 0.0)
                {
                    if (foodGatherers <= 0)
                        continue;
                    foodGatherers--;
                    aiTaskUnitWork(villager, resource);
                    break;
                }
                if (kbUnitGetCurrentInventory(resource, cResourceWood) > 0.0)
                {
                    if (woodGatherers <= 0)
                        continue;
                    woodGatherers--;
                    aiTaskUnitWork(villager, resource);
                    break;
                }
                if (kbUnitGetCurrentInventory(resource, cResourceGold) > 0.0)
                {
                    if (goldGatherers <= 0)
                        continue;
                    goldGatherers--;
                    aiTaskUnitWork(villager, resource);
                    break;
                }
            }
        }
    }
}


rule MonitorVillagerPopulation inactive minInterval 1 runImmediately
{
    // TODO: handle each Pa separately
    static int maintainplan = -1;
    
    if (aiPlanGetState(maintainplan) == -1)
    {
        aiPlanDestroy(maintainplan);
        maintainplan = planMaintain(cUnitTypePOLYvillager, 80, cRootEscrowID, 2, -1, 6, true);
    }
}


rule MonitorSettlement inactive minInterval 5
{
    int plan = -1;
    int queue = kbUnitCount(cMyID, cUnitTypeMarket, cUnitStateABQ);
    int planned = 1 - queue;
    if ((planned >= 1) && (aiPlanGetIDByTypeAndVariableType(cPlanBuild, cBuildPlanBuildingTypeID, cUnitTypeMarket, true) <= -1))
    {
        plan = planBuild(cUnitTypeMarket, kbBaseGetMainID(cMyID), cUnitTypePOLYvillager, 1, 50, cRootEscrowID);
        aiPlanSetEventHandler(plan, cPlanEventStateChange, "HandlerSettlementConstructionState");
    }
    
    queue = kbUnitCount(cMyID, cUnitTypeMaoriPa, cUnitStateABQ);
    planned = kbGetBuildLimit(cMyID, cUnitTypeMaoriPa) - queue;
    if ((planned >= 1) && (aiPlanGetIDByTypeAndVariableType(cPlanBuild, cBuildPlanBuildingTypeID, cUnitTypeMaoriPa, true) <= -1))
    {
        plan = planBuildFarFromEachOther(cUnitTypeMaoriPa, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)), cUnitTypePOLYvillager, 8, cRootEscrowID, 100);
        aiPlanSetEventHandler(plan, cPlanEventStateChange, "HandlerSettlementConstructionState");
    }
    
    queue = kbUnitCount(cMyID, cUnitTypeSandalwoodGrove, cUnitStateABQ);
    planned = kbGetBuildLimit(cMyID, cUnitTypeSandalwoodGrove) - queue;
    if ((planned >= 1) && (aiPlanGetIDByTypeAndVariableType(cPlanBuild, cBuildPlanBuildingTypeID, cUnitTypeSandalwoodGrove, true) <= -1))
    {
        plan = planBuild(cUnitTypeSandalwoodGrove, kbBaseGetMainID(cMyID), cUnitTypePOLYvillager, 2, 50, cRootEscrowID);
        aiPlanSetEventHandler(plan, cPlanEventStateChange, "HandlerSettlementConstructionState");
    }
    
    queue = kbUnitCount(cMyID, cUnitTypePOLYTrainingGround, cUnitStateABQ);
    planned = kbGetAge() - queue;
    if ((planned >= 1) && (aiPlanGetIDByTypeAndVariableType(cPlanBuild, cBuildPlanBuildingTypeID, cUnitTypePOLYTrainingGround, true) <= -1))
    {
        plan = planBuild(cUnitTypePOLYTrainingGround, kbBaseGetMainID(cMyID), cUnitTypePOLYvillager, 2, 70, cRootEscrowID);
        aiPlanSetEventHandler(plan, cPlanEventStateChange, "HandlerSettlementConstructionState");
    }
    
    queue = kbUnitCount(cMyID, cUnitTypePOLYSiegeWorkshop, cUnitStateABQ);
    planned = kbGetAge() - queue - 1;
    if ((planned >= 1) && (aiPlanGetIDByTypeAndVariableType(cPlanBuild, cBuildPlanBuildingTypeID, cUnitTypePOLYSiegeWorkshop, true) <= -1))
    {
        plan = planBuild(cUnitTypePOLYSiegeWorkshop, kbBaseGetMainID(cMyID), cUnitTypePOLYvillager, 2, 70, cRootEscrowID);
        aiPlanSetEventHandler(plan, cPlanEventStateChange, "HandlerSettlementConstructionState");
    }
}


void HandlerSettlementConstructionState(int plan = -1)
{
    int building = aiPlanGetVariableInt(plan, cBuildPlanBuildingTypeID, 0);
    if (building == cUnitTypeMaoriPa)
    {
        if (aiPlanGetState(plan) == cPlanStateBuild)
        {
            vector buildingLoc = kbBuildingPlacementGetResultPosition(aiPlanGetVariableInt(plan, cBuildPlanBuildingPlacementID, 0));
            for(player = 1 ; < cNumberPlayers)
            {
                if (player == cMyID)
                    continue;
                if (kbIsPlayerEnemy(player))
                    continue;
                aiCommsSendStatementWithVector(player, -1, buildingLoc);
                aiChat(player, "Yo, "+kbGetPlayerName(player)+", I'm gonna make another settlement. I'm probably gonna need some protection :D");
            }
        }
    }
}


rule MonitorResources inactive minInterval 10
{
    if (kbUnitCount(cMyID, cUnitTypeKumaraField, cUnitStateABQ) > kbUnitCount(cMyID, cUnitTypeAbstractVillager, cUnitStateAlive) * 0.06)
    {
        if (kbGetAge() >= cAge5)
            xsSetRuleMinIntervalSelf(60);
        return;
    }
    
    if (kbResourceGet(cResourceWood) < 200.0)
        return;
    
    if (kbGetAge() <= cAge4)
    {
        if (kbGetAmountValidResources(kbBaseGetMainID(cMyID), cResourceFood, cAIResourceSubTypeHunt, 120.0) > 1000.0)
            return;
        if (kbGetAmountValidResources(kbBaseGetMainID(cMyID), cResourceFood, cAIResourceSubTypeHunt, 150.0) > 2000.0)
            return;
    }
    
    int plan = aiPlanGetIDByTypeAndVariableType(cPlanBuild, cBuildPlanBuildingTypeID, cUnitTypeKumaraField);
    if (plan >= 0)
        return;
    
    plan = planBuild(cUnitTypeKumaraField, kbBaseGetMainID(cMyID), cUnitTypePOLYvillager, 10, 60);
    gFarmingMode = true;
}


rule MonitorVoyages active minInterval 5 runImmediately group StartupMonitors
{
    static int age = 0;
    static int plan = -1;
    int destination = cUnitTypePOLYVMNewSouthWales2;
    int rangatira = findUnit1(cUnitTypeRangatira);
    vector rangatiraLoc = kbUnitGetPosition(rangatira);
    
    if (kbGetAge() == age)
    {
        if (age == cAge1) destination = cUnitTypePOLYVMNewSouthWales2;
        if (age == cAge2) destination = cUnitTypePOLYVMVanDiemensLand3;
        if (age == cAge3) destination = cUnitTypePOLYVMChathamIslands4;
        if (age == cAge4) destination = cUnitTypePOLYVMNewSouthWales5;
        
        if (kbCanAffordUnit(destination, cRootEscrowID))
        {
            aiPlanDestroy(plan);
            plan = planBuildAt(destination, rangatiraLoc, cUnitTypeRangatira, 1, 100);
            aiPlanSetEventHandler(plan, cPlanEventStateChange, "HandlerVoyageState");
            age++;
        }
    }
    
    gAgingUp = (kbUnitCount(cMyID, cUnitTypeAbstractWonder, cUnitStateBuilding) >= 1);
}


void HandlerVoyageState(int plan = -1)
{
    
}


rule MonitorEconomicTechnologies inactive minInterval 10
{
    if (xsGetTime() >= 1000 * 60 * (15 + aiRandInt(16)))
        planResearch(cTechBigBountifulIslandsMaori);
    
    planResearch(cTechWhanau);
    planResearch(cTechKumaraStorage);
    planResearch(cTechHapu);
    planResearch(cTechHangi);
    planResearch(cTechIwi);
    planResearch(cTechBirdSnares);
    planResearch(cTechStoneAdzes);
    planResearch(cTechBasaltQuarry);
    planResearch(cTechBarkCloth);
    planResearch(cTechRatSnares);
    planResearch(cTechEuropeanAxes);
    planResearch(cTechPetroglyphs);
    planResearch(cTechTattooing);
    planResearch(cTechWoodCarving);
    planResearch(cTechDryStoneConstruction);
    planResearch(cTechTradeMission);
    planResearch(cTechSandalwoodTrade);
    
    if (gFarmingMode)
    {
        planResearch(cTechWovenBaskets);
        planResearch(cTechPohaKelpBags);
    }
}


rule MonitorMilitaryTechnologies inactive minInterval 20
{
    if (kbGetAge() <= cAge1)
        return;
    
    planResearch(cTechPallisades);
    planResearch(cTechFightingPlatforms);
    planResearch(cTechPakehaCannons);
    planResearch(cTechTuparaMarauders);
    planResearch(cTechStrongTuparaMarauders);
    planResearch(cTechExpertTuparaMarauders);
    planResearch(cTechTewhatewhaSpearmen);
    planResearch(cTechStrongTewhatewhaSpearmen);
    planResearch(cTechExpertTewhatewhaSpearmen);
}


rule MonitorBigButtonTechnologies inactive minInterval 120
{
    if (xsGetTime() >= 1000 * 60 * (15 + aiRandInt(16)))
        planResearch(cTechBigPaTreatyOfWaitangi);
    if (xsGetTime() >= 1000 * 60 * (15 + aiRandInt(16)))
        planResearch(cTechBigBountifulIslandsMaori);
    
    planResearch(cTechBigMaraeRuamoko);
    planResearch(cTechBigMaoriAgriculturalRevolution);
    planResearch(cTechBigRetreatAtRuapekapeka);
}


rule MonitorMilitaryPopulation inactive minInterval 5
{
    if (aiPlanGetState(gMarksmenPlan) == -1)
    {
        aiPlanDestroy(gMarksmenPlan);
        gMarksmenPlan = planMaintain(cUnitTypePOLYMarauder, kbGetAge() * 15, cRootEscrowID, 10, -1, 50, true);
    }
    
    if (aiPlanGetState(gSpearmenPlan) == -1)
    {
        aiPlanDestroy(gSpearmenPlan);
        gSpearmenPlan = planMaintain(cUnitTypePOLYSpearman, kbGetAge() * 15, cRootEscrowID, 10, -1, 50, true);
    }
    
    if (aiPlanGetState(gBallistaPlan) == -1)
    {
        aiPlanDestroy(gBallistaPlan);
        gBallistaPlan = planMaintain(cUnitTypePolyStoneThrower, kbGetAge() * 2, cRootEscrowID, 5, -1, 10, true);
    }
    
    aiPlanSetVariableInt(gMarksmenPlan, cTrainPlanNumberToMaintain, 0, kbGetAge() * 15);
    aiPlanSetVariableInt(gSpearmenPlan, cTrainPlanNumberToMaintain, 0, kbGetAge() * 15);
    aiPlanSetVariableInt(gBallistaPlan, cTrainPlanNumberToMaintain, 0, kbGetAge() * 2);
}


rule MonitorOffensiveOperations inactive minInterval 30 runImmediately
{
    if (kbGetAge() <= cAge1)
        return;
    
    static bool refill = true;
    int planID = -1;
    int targetID = -1;
    int unitID = -1;
    vector v = cOriginVector;
    int temp = -1;
    float best_score = 0;
    float s = 0;
    for(i = 0 ; < 50)
    {
        temp = findUnitByLocation1(cUnitTypeHasBountyValue, cPlayerRelationEnemyNotGaia, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)), 800.0, i);
        if (temp == -1)
            break;
        v = kbUnitGetPosition(temp);
        s = countUnitsByLocation(cUnitTypeAbstractVillager, cPlayerRelationEnemyNotGaia, v, 40.0);
        s = s + countUnitsByLocation(cUnitTypeLogicalTypeLandMilitary, cPlayerRelationEnemyNotGaia, v, 40.0);
        s = s / xsVectorLength(v - kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)));
        if (s > best_score)
        {
            best_score = s;
            unitID = temp;
        }
    }
    v = kbUnitGetPosition(unitID);
    int playerID = kbUnitGetPlayerID(unitID);
    if(playerID == -1)
    {
        for(i = 1; < cNumberPlayers)
        {
            if (kbIsPlayerEnemy(i))
                break;
        }
        playerID = i;
    }
    
    if(v == cInvalidVector)
        v = kbUnitGetPosition(findUnit1(cUnitTypeBuilding, cPlayerRelationEnemyNotGaia));
    
    for(i = 0 ; < aiPlanGetNumber(cPlanAttack, -1, true))
    {
        planID = aiPlanGetIDByIndex(cPlanAttack, -1, true, i);
        if (aiPlanGetNumberUnits(planID, cUnitTypeLogicalTypeLandMilitary) == 0)
        {
            aiPlanDestroy(planID);
            continue;
        }
        if (countUnitsByLocation(cUnitTypeUnit, cPlayerRelationEnemyNotGaia, aiPlanGetVariableVector(planID, cAttackPlanAttackPoint, 0), 45.0) < 1)
        {
            aiPlanSetVariableInt(planID, cAttackPlanPlayerID, 0, playerID);
            aiPlanSetVariableVector(planID, cAttackPlanAttackPoint, 0, v);
        }
        targetID = aiPlanGetVariableInt(planID, cAttackPlanTargetID, 0);
        if (kbUnitIsType(targetID, cUnitTypeAbstractWall) == true)
        {
            aiPlanSetVariableInt(planID, cAttackPlanPlayerID, 0, playerID);
            aiPlanSetVariableInt(planID, cAttackPlanSpecificTargetID, 0, unitID);
        }
        else if (targetID == -1)
        {
            aiPlanSetVariableInt(planID, cAttackPlanPlayerID, 0, playerID);
            aiPlanSetVariableInt(planID, cAttackPlanSpecificTargetID, 0, unitID);
        }
        else if(kbUnitGetCurrentHitpoints(targetID) < 0.1 )
        {
            aiPlanSetVariableInt(planID, cAttackPlanPlayerID, 0, playerID);
            aiPlanSetVariableInt(planID,cAttackPlanSpecificTargetID, 0, unitID);
        }
        else if(kbUnitIsType(targetID, cUnitTypeHero) == true)
        {
            aiPlanSetVariableInt(planID, cAttackPlanPlayerID, 0, playerID);
            aiPlanSetVariableInt(planID, cAttackPlanSpecificTargetID, 0, unitID);
        }
        aiPlanSetNoMoreUnits(planID, true);
    }
    
    if (aiNumberUnassignedUnits(cUnitTypeLogicalTypeLandMilitary) == 0)
    {
        refill = true;
        return;
    }
    
    if (refill)
    {
        if (aiNumberUnassignedUnits(cUnitTypeLogicalTypeLandMilitary) < 30 + (kbGetAge() - 1) * 15)
            return;
        if (aiPlanGetNumber(cPlanAttack, -1, true) >= 1)
            return;
    }
    
    refill = false;
    
    if (aiPlanGetNumber(cPlanAttack, -1, true) < 5)
    {
        int attackplan = aiPlanCreate("Attack Range", cPlanAttack);
        aiPlanSetDesiredPriority(attackplan, 75);
        aiPlanSetUnitStance(attackplan, cUnitStanceAggressive);
        aiPlanSetAllowUnderAttackResponse(attackplan, true);
        aiPlanSetVariableInt(attackplan, cAttackPlanPlayerID, 0, playerID);
        aiPlanSetVariableVector(attackplan, cAttackPlanAttackPoint, 0, v);
        aiPlanSetVariableFloat(attackplan, cAttackPlanAttackPointEngageRange, 0, 60.0);
        aiPlanSetNumberVariableValues(attackplan, cAttackPlanTargetTypeID, 3, true);
        aiPlanSetVariableInt(attackplan, cAttackPlanTargetTypeID, 0, cUnitTypeAbstractInfantry);
        aiPlanSetVariableInt(attackplan, cAttackPlanTargetTypeID, 1, cUnitTypeAbstractVillager);
        aiPlanSetVariableInt(attackplan, cAttackPlanTargetTypeID, 2, cUnitTypeLogicalTypeBuildingsNotWalls);
        aiPlanSetVariableInt(attackplan, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternBest);
        aiPlanSetVariableBool(attackplan, cAttackPlanMoveAttack, 0, true);
        aiPlanSetVariableInt(attackplan, cAttackPlanRefreshFrequency, 0, 5);
        aiPlanSetVariableInt(attackplan, cAttackPlanHandleDamageFrequency, 0, 10);
        aiPlanSetVariableInt(attackplan, cAttackPlanBaseAttackMode, 0, cAttackPlanBaseAttackModeRandom);
        aiPlanSetVariableInt(attackplan, cAttackPlanRetreatMode, 0, cAttackPlanRetreatModeNone);
        aiPlanSetVariableInt(attackplan, cAttackPlanGatherWaitTime, 0, 0);
        int num = kbUnitCount(cMyID, cUnitTypePOLYMarauder, cUnitStateAlive);
        aiPlanAddUnitType(attackplan, cUnitTypePOLYMarauder, 1, num, num);
        // aiPlanSetNoMoreUnits(attackplan, true);
        aiPlanSetActive(attackplan, true);
        
        attackplan = aiPlanCreate("Attack Melee", cPlanAttack);
        aiPlanSetDesiredPriority(attackplan, 75);
        aiPlanSetUnitStance(attackplan, cUnitStanceAggressive);
        aiPlanSetAllowUnderAttackResponse(attackplan, true);
        aiPlanSetVariableInt(attackplan, cAttackPlanPlayerID, 0, playerID);
        aiPlanSetVariableVector(attackplan, cAttackPlanAttackPoint, 0, v);
        aiPlanSetVariableFloat(attackplan, cAttackPlanAttackPointEngageRange, 0, 60.0);
        aiPlanSetNumberVariableValues(attackplan, cAttackPlanTargetTypeID, 3, true);
        aiPlanSetVariableInt(attackplan, cAttackPlanTargetTypeID, 0, cUnitTypeAbstractCavalry);
        aiPlanSetVariableInt(attackplan, cAttackPlanTargetTypeID, 1, cUnitTypeAbstractVillager);
        aiPlanSetVariableInt(attackplan, cAttackPlanTargetTypeID, 2, cUnitTypeLogicalTypeBuildingsNotWalls);
        aiPlanSetVariableInt(attackplan, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternBest);
        aiPlanSetVariableBool(attackplan, cAttackPlanMoveAttack, 0, true);
        aiPlanSetVariableInt(attackplan, cAttackPlanRefreshFrequency, 0, 5);
        aiPlanSetVariableInt(attackplan, cAttackPlanHandleDamageFrequency, 0, 10);
        aiPlanSetVariableInt(attackplan, cAttackPlanBaseAttackMode, 0, cAttackPlanBaseAttackModeRandom);
        aiPlanSetVariableInt(attackplan, cAttackPlanRetreatMode, 0, cAttackPlanRetreatModeNone);
        aiPlanSetVariableInt(attackplan, cAttackPlanGatherWaitTime, 0, 0);
        num = kbUnitCount(cMyID, cUnitTypePOLYSpearman, cUnitStateAlive);
        aiPlanAddUnitType(attackplan, cUnitTypePOLYSpearman, 1, num, num);
        // aiPlanSetNoMoreUnits(attackplan, true);
        aiPlanSetActive(attackplan, true);
        
        attackplan = aiPlanCreate("Attack Siege", cPlanAttack);
        aiPlanSetDesiredPriority(attackplan, 75);
        aiPlanSetUnitStance(attackplan, cUnitStanceAggressive);
        aiPlanSetAllowUnderAttackResponse(attackplan, true);
        aiPlanSetVariableInt(attackplan, cAttackPlanPlayerID, 0, playerID);
        aiPlanSetVariableVector(attackplan, cAttackPlanAttackPoint, 0, v);
        aiPlanSetVariableFloat(attackplan, cAttackPlanAttackPointEngageRange, 0, 60.0);
        aiPlanSetNumberVariableValues(attackplan, cAttackPlanTargetTypeID, 3, true);
        aiPlanSetVariableInt(attackplan, cAttackPlanTargetTypeID, 0, cUnitTypeLogicalTypeBuildingsNotWalls);
        aiPlanSetVariableInt(attackplan, cAttackPlanTargetTypeID, 1, cUnitTypeAbstractVillager);
        aiPlanSetVariableInt(attackplan, cAttackPlanTargetTypeID, 2, cUnitTypeLogicalTypeBuildingsNotWalls);
        aiPlanSetVariableInt(attackplan, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternBest);
        aiPlanSetVariableBool(attackplan, cAttackPlanMoveAttack, 0, true);
        aiPlanSetVariableInt(attackplan, cAttackPlanRefreshFrequency, 0, 5);
        aiPlanSetVariableInt(attackplan, cAttackPlanHandleDamageFrequency, 0, 10);
        aiPlanSetVariableInt(attackplan, cAttackPlanBaseAttackMode, 0, cAttackPlanBaseAttackModeRandom);
        aiPlanSetVariableInt(attackplan, cAttackPlanRetreatMode, 0, cAttackPlanRetreatModeNone);
        aiPlanSetVariableInt(attackplan, cAttackPlanGatherWaitTime, 0, 0);
        num = kbUnitCount(cMyID, cUnitTypePolyStoneThrower, cUnitStateAlive);
        aiPlanAddUnitType(attackplan, cUnitTypePolyStoneThrower, 1, num, num);
        // aiPlanSetNoMoreUnits(attackplan, true);
        aiPlanSetActive(attackplan, true);
    }
}


rule MonitorRoutes active minInterval 5
{
    return; // TODO
    
    kbLookAtAllUnitsOnMap();
    
    if (aiPlanGetIDByTypeAndVariableType(cPlanData, -1, -1, false) == -1)
    {
        int db = aiPlanCreate("Data", cPlanData);
        aiPlanAddUserVariableInt(db, 0, "a", 1);
        aiPlanSetUserVariableInt(db, 0, 0, 0);
    }
    else
        db = aiPlanGetIDByTypeAndVariableType(cPlanData, -1, -1, false);
    
    
}


rule MonitorCommercialTradingPosts inactive minInterval 60
{
    if (kbUnitCount(0, cUnitTypeypSocketTradeRoute) == 0)
    {
        if (kbUnitCount(0, cUnitTypeSocketTradeRoute) == 0)
        {
            xsDisableSelf();
            return;
        }
    }
    if (aiPlanGetIDByTypeAndVariableType(cPlanBuild, cBuildPlanBuildingTypeID, cUnitTypeTradingPost, true) >= 0)
        return;
    if (kbUnitCount(cMyID, cUnitTypeMaoriPa, cUnitStateABQ) < kbGetBuildLimit(cMyID, cUnitTypeMaoriPa))
        return;
    if (kbUnitCount(cMyID, cUnitTypePOLYTrainingGround, cUnitStateABQ) == 0)
        return;
    if (kbUnitCount(cMyID, cUnitTypeTradingPostTravois, cUnitStateAlive) == 0)
    {
        if (aiPlanGetIDByTypeAndVariableType(cPlanTrain, cTrainPlanUnitType, cUnitTypeTradingPostTravois, true) == -1)
            planMaintain(cUnitTypeTradingPostTravois, 1, cRootEscrowID, 1, cUnitTypeRangatira);
        return;
    }
    
    for(iSocket = 0 ; < kbUnitCount(0, cUnitTypeSocket))
    {
        int socket = findUnitByLocation1(cUnitTypeSocket, 0, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)), 300.0, iSocket);
        if (kbUnitIsType(socket, cUnitTypeNativeSocket) == false)
        {
            if (countUnitsByLocation(cUnitTypeVictoryPointBuilding, cPlayerRelationAny, kbUnitGetPosition(socket)) == 0)
            {
                int plan = aiPlanCreate("Build TradingPost", cPlanBuild);
                aiPlanSetDesiredPriority(plan, 90);
                aiPlanSetEscrowID(plan, cRootEscrowID);
                aiPlanAddUnitType(plan, cUnitTypeTradingPostTravois, 1, 1, 1);
                aiPlanSetVariableInt(plan, cBuildPlanBuildingTypeID, 0, cUnitTypeTradingPost);
                aiPlanSetVariableInt(plan, cBuildPlanSocketID, 0, socket);
                aiPlanSetActive(plan, true);
                xsEnableRule("MonitorCommercialRoutes");
            }
        }
    }
}


rule MonitorCommercialRoutes inactive minInterval 60 runImmediately
{
    
}


rule MonitorNativeTradingPosts inactive minInterval 60
{
    if (kbUnitCount(0, cUnitTypeNativeSocket) == 0)
    {
        xsDisableSelf();
        return;
    }
    if (aiPlanGetIDByTypeAndVariableType(cPlanBuild, cBuildPlanBuildingTypeID, cUnitTypeTradingPost, true) >= 0)
        return;
    if (kbUnitCount(cMyID, cUnitTypeMaoriPa, cUnitStateABQ) < kbGetBuildLimit(cMyID, cUnitTypeMaoriPa))
        return;
    if (kbUnitCount(cMyID, cUnitTypePOLYTrainingGround, cUnitStateABQ) == 0)
        return;
    if (kbUnitCount(cMyID, cUnitTypeTradingPostTravois, cUnitStateAlive) == 0)
    {
        if (aiPlanGetIDByTypeAndVariableType(cPlanTrain, cTrainPlanUnitType, cUnitTypeTradingPostTravois, true) == -1)
            planMaintain(cUnitTypeTradingPostTravois, 1, cRootEscrowID, 1, cUnitTypeRangatira);
        return;
    }
    
    for(iSocket = 0 ; < kbUnitCount(0, cUnitTypeNativeSocket))
    {
        int socket = findUnitByLocation1(cUnitTypeNativeSocket, 0, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)), 300.0, iSocket);
        if (countUnitsByLocation(cUnitTypeVictoryPointBuilding, cPlayerRelationAny, kbUnitGetPosition(socket)) == 0)
        {
            int plan = aiPlanCreate("Build TradingPost", cPlanBuild);
            aiPlanSetDesiredPriority(plan, 90);
            aiPlanSetEscrowID(plan, cRootEscrowID);
            aiPlanAddUnitType(plan, cUnitTypeTradingPostTravois, 1, 1, 1);
            aiPlanSetVariableInt(plan, cBuildPlanBuildingTypeID, 0, cUnitTypeTradingPost);
            aiPlanSetVariableInt(plan, cBuildPlanSocketID, 0, socket);
            aiPlanSetActive(plan, true);
            xsEnableRule("MonitorNativeAlliances");
        }
    }
}


rule MonitorNativeAlliances inactive minInterval 20 runImmediately
{
    kbUnitPickResetAll(cUnitPicker);
    kbUnitPickSetPreferenceWeight(cUnitPicker, 1.);
    kbUnitPickSetCombatEfficiencyWeight(cUnitPicker, 0.);
    kbUnitPickSetCostWeight(cUnitPicker, 0.);
    kbUnitPickSetMovementType(cUnitPicker, cMovementTypeLand);
    kbUnitPickSetPreferenceFactor(cUnitPicker, cUnitTypeAbstractNativeWarrior, 1.);
    int num_found = kbUnitPickRun(cUnitPicker);
    
    for(iNative = 0 ; < num_found)
    {
        int native = kbUnitPickGetResult(cUnitPicker, iNative);
        if (aiPlanGetIDByTypeAndVariableType(cPlanTrain, cTrainPlanUnitType, native, true) >= 0)
        {
            aiPlanSetVariableInt(aiPlanGetIDByTypeAndVariableType(cPlanTrain, cTrainPlanUnitType, native, true), 
                                 cTrainPlanNumberToMaintain, 0, kbGetBuildLimit(cMyID, native));
            int upgrade = kbTechTreeGetCheapestUnitUpgrade(native);
            if (upgrade == -1)
                continue;
            if (aiPlanGetIDByTypeAndVariableType(cPlanResearch, cResearchPlanTechID, upgrade, true) >= 0)
                continue;
            planResearch(upgrade);
            
            upgrade = kbTechTreeGetCheapestUnitUpgrade(cUnitTypeAbstractNativeWarrior);
            if (upgrade == -1)
                continue;
            if (aiPlanGetIDByTypeAndVariableType(cPlanResearch, cResearchPlanTechID, upgrade, true) >= 0)
                continue;
            planResearch(upgrade);
            continue;
        }
        planMaintain(native, kbGetBuildLimit(cMyID, native), cRootEscrowID, 10, -1, 20, true);
    }
}


rule MonitorReligion inactive minInterval 30
{
    if (kbGetAge() <= cAge2)
        return;
    
    if (kbUnitCount(cMyID, cUnitTypePOLYTemple, cUnitStateAlive) >= 1)
    {
        // TODO - Train Priests
        
        int religion = -1;
        if (kbTechGetStatus(cTechRELIGIONAtheism) == cTechStatusActive)
            religion = cTechRELIGIONAtheism;
        if (kbTechGetStatus(cTechRELIGIONAnglican) == cTechStatusActive)
            religion = cTechRELIGIONAnglican;
        if (kbTechGetStatus(cTechRELIGIONManaismMaori) == cTechStatusActive)
            religion = cTechRELIGIONManaismMaori;
        gWeAreReligious = (religion >= 0) && (religion != cTechRELIGIONAtheism);
        
        switch(religion)
        {
            case cTechRELIGIONAtheism:
            {
                planResearch(cTechRELIGIONAtheism02);
                planResearch(cTechRELIGIONAtheism04);
                planResearch(cTechRELIGIONAtheism05);
                if (kbGetAge() <= cAge4)
                    planResearch(cTechRELIGIONAtheism06);
                break;
            }
            case cTechRELIGIONAnglican:
            {
                planResearch(cTechRELIGIONAnglican01);
                planResearch(cTechRELIGIONAnglican02);
                // planResearch(cTechRELIGIONAnglican03); // TODO - Priests pray
                break;
            }
            case cTechRELIGIONManaismMaori:
            {
                planResearch(cTechManaismTiki);
                if (kbUnitCount(cMyID, cUnitTypeHomeCityWaterSpawnFlag, cUnitStateAlive) >= 1)
                {
                    planResearch(cTechManaismMauisFishhook);
                    planResearch(cTechManaismTangaroa);
                }
                planResearch(cTechManaismSocialCastes);
                planResearch(cTechManaismTane);
                break;
            }
        }
        return;
    }
    
    if (kbUnitCount(cMyID, cUnitTypePOLYTemple, cUnitStateABQ) >= 1)
        return;
    
    if (aiPlanGetIDByTypeAndVariableType(cPlanBuild, cBuildPlanBuildingTypeID, cUnitTypePOLYTemple, true) >= 0)
        return;
    
    int plan = planBuild(cUnitTypePOLYTemple, kbBaseGetMainID(cMyID), cUnitTypePOLYvillager);
    aiPlanSetEventHandler(plan, cPlanEventStateChange, "HandlerSettlementConstructionState");
}

